/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.vwbe.impl.resource_fetcher;

import elaborate.vwbe.resource_fetcher.HttpAuthenticationScheme;
import elaborate.vwbe.resource_fetcher.ResourceFetcherCallback;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.xml.sax.SAXException;

/**
 * Create an instance of HttpClient thats supports
 * Basic authentication
 * Digest authentication
 * TLS/SSL connection(according to the given url)
 * @author lendle
 */
public class HttpClientFactory {

    public static HttpClient createHttpClient(URL url, final ResourceFetcherCallback callback) throws MalformedURLException, NoSuchAlgorithmException, KeyManagementException, UnsupportedEncodingException, FileNotFoundException, ParserConfigurationException, SAXException, IOException, XPathExpressionException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        if (url.getProtocol().toLowerCase().equals("https")) {
            //add ssl support
            httpClient = addSSLSupport(httpClient, url);
        }

        final Credentials credential = (callback.getUserName()!=null)?new UsernamePasswordCredentials(callback.getUserName(), callback.getPassword()):null;

        //use preemptive authentication, not sure why must use this
        //however, the procedure failed if not using this
        HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {

            public void process(
                    final HttpRequest request,
                    final HttpContext context) throws HttpException, IOException {

                AuthState authState = (AuthState) context.getAttribute(
                        ClientContext.TARGET_AUTH_STATE);
                CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
                        ClientContext.CREDS_PROVIDER);
                HttpHost targetHost = (HttpHost) context.getAttribute(
                        ExecutionContext.HTTP_TARGET_HOST);

                // If not auth scheme has been initialized yet
                if (authState.getAuthScheme() == null) {
                    AuthScope authScope = new AuthScope(
                            targetHost.getHostName(),
                            targetHost.getPort());
                    if (HttpAuthenticationScheme.Basic.equals(callback.getHttpAuthenticationScheme())) {
                        authState.setAuthScheme(new BasicScheme());
                    } else {
                        authState.setAuthScheme(new DigestScheme());
                    }
                }
                if(callback.getUserName()!=null){
                    authState.setCredentials(credential);
                }

            }
        };
        httpClient.addRequestInterceptor(preemptiveAuth, 0);
        return httpClient;
    }

    private static DefaultHttpClient addSSLSupport(DefaultHttpClient httpClient, URL url) throws NoSuchAlgorithmException, KeyManagementException {
        X509TrustManager trustManager = new X509TrustManager() {

            public void checkClientTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                // Don't do anything.
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                // Don't do anything.
            }

            public X509Certificate[] getAcceptedIssuers() {
                // Don't do anything.
                return null;
            }
        };
        SSLContext sslcontext = SSLContext.getInstance("TLS");
        sslcontext.init(null, new TrustManager[]{trustManager}, null);
        SSLSocketFactory sf = new SSLSocketFactory(sslcontext);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        ClientConnectionManager ccm = httpClient.getConnectionManager();
        SchemeRegistry schemeRegistry = ccm.getSchemeRegistry();
        schemeRegistry.register(new Scheme(url.getProtocol(), sf, url.getPort()));
        httpClient = new DefaultHttpClient(ccm, httpClient.getParams());
        return httpClient;
    }
}
